home *** CD-ROM | disk | FTP | other *** search
/ Over 1,000 Windows 95 Programs / Over 1000 Windows 95 Programs (Microforum) (Disc 1).iso / 0525 / grep.c_ / grep.c
Text File  |  1997-04-03  |  16KB  |  613 lines

  1.  /*
  2.  *
  3.  *
  4.  * The    information  in  this  document  is  subject  to  change
  5.  * without  notice  and  should not be construed as a commitment
  6.  * by Digital Equipment Corporation or by DECUS.
  7.  *
  8.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  9.  * assume any responsibility for the use or reliability of  this
  10.  * document or the described software.
  11.  *
  12.  *    Copyright (C) 1980, DECUS
  13.  *
  14.  *
  15.  * General permission to copy or modify, but not for profit,  is
  16.  * hereby  granted,  provided that the above copyright notice is
  17.  * included and reference made to  the    fact  that  reproduction
  18.  * privileges were granted by DECUS.
  19.  *
  20.  */
  21. #include <dir.h>
  22. #include <stdio.h>
  23. #include <ctype.h>
  24.  /*
  25.  * grep.
  26.  *
  27.  * Runs on the Decus compiler or on vms.
  28.  * Converted for BDS compiler (under CP/M-80), 20-Jan-83, by Chris Kern.
  29.  *
  30.  * Converted to IBM PC with CI-C86 C Compiler June 1983 by David N. Smith
  31.  *
  32.  * On vms, define as:
  33.  *
  34.  *    grep :== "$disk:[account]grep"     (native)
  35.  *    grep :== "$disk:[account]grep grep"     (Decus)
  36.  *
  37.  * See below for more information.
  38.  *
  39.  */
  40. char    *documentation[] = {
  41. "grep searches a file for a given pattern.  Execute by",
  42. "   grep [flags] regular_expression file_list",
  43. "",
  44. "Flags are single characters preceeded by '-':",
  45. "   -c      Only a count of matching lines is printed",
  46. "   -f      Print file name for matching lines switch, see below",
  47. "   -n      Each line is preceeded by its line number",
  48. "   -v      Only print non-matching lines",
  49. "",
  50. "The file_list is a list of files (wildcards are acceptable on RSX modes).",
  51. "",
  52. "The file name is normally printed if there is a file given.",
  53. "The -f flag reverses this action (print name no file, not if more).",
  54. "",
  55. 0 };
  56. char    *patdoc[] = {
  57. "The regular_expression defines the pattern to search for.  Upper- and",
  58. "lower-case are always ignored.  Blank lines never match.  The expression",
  59. "should be quoted to prevent file-name translation.",
  60. "x      An ordinary character (not mentioned below) matches that character.",
  61. "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.",
  62. "'^'    A circumflex at the beginning of an expression matches the",
  63. "       beginning of a line.",
  64. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  65. "'.'    A period matches any character except \"new-line\".",
  66. "':a'   A colon matches a class of characters described by the following",
  67. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  68. "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
  69. "': '     other control characters, such as new-line.",
  70. "'*'    An expression followed by an asterisk matches zero or more",
  71. "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
  72. "       \"foo\", etc.",
  73. "'+'    An expression followed by a plus sign matches one or more",
  74. "       occurrances of that expression: \"fo+\" matches \"fo\", etc.",
  75. "'-'    An expression followed by a minus sign optionally matches",
  76. "       the expression.",
  77. "'[]'   A string enclosed in square brackets matches any character in",
  78. "       that string, but no others.  If the first character in the",
  79. "       string is a circumflex, the expression matches any character",
  80. "       except \"new-line\" and the characters in the string.  For",
  81. "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
  82. "       matches \"abc\" but not \"axb\".  A range of characters may be",
  83. "       specified by two characters separated by \"-\".  Note that,",
  84. "       [a-z] matches alphabetics, while [z-a] never matches.",
  85. "The concatenation of regular expressions is a regular expression.",
  86. 0};
  87. /*$ifdef  vms                     */
  88. /*$define VMS            VMS native compiler  */
  89. /*$define error(s)   _error(s)             */
  90. /*$endif                     */
  91. #define LMAX    512
  92. #define PMAX    256
  93. #define CHAR    1
  94. #define BOL    2
  95. #define EOL    3
  96. #define ANY    4
  97. #define CLASS    5
  98. #define NCLASS    6
  99. #define STAR    7
  100. #define PLUS    8
  101. #define MINUS    9
  102. #define ALPHA    10
  103. #define DIGIT    11
  104. #define NALPHA    12
  105. #define PUNCT    13
  106. #define RANGE    14
  107. #define ENDPAT    15
  108. int    cflag;
  109. int    fflag;
  110. int    nflag;
  111. int    vflag;
  112. int    nfile;
  113. int    debug    =    0;       /* Set for debug code      */
  114. char    *pp;
  115. #ifndef vms
  116. char    file_name[81];
  117. #endif
  118. char    lbuf[LMAX];
  119. char    pbuf[PMAX];
  120. /*******************************************************/
  121. main(argc, argv)
  122. char *argv[];
  123. {
  124.    register char   *p;
  125.    register int    c, i;
  126.    int           gotpattern;
  127.    int           gotcha;
  128.    FILE        *f;
  129.  
  130. //-- JAJ wildcard support
  131.    int err;
  132.    int count = 0;
  133.    struct ffblk findbuf;
  134.  
  135.    if (argc <= 1)
  136.       usage("No arguments");
  137.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  138.       help(documentation);
  139.       help(patdoc);
  140.       return;
  141.       }
  142.    nfile = argc-1;
  143.    gotpattern = 0;
  144.    for (i=1; i < argc; ++i) {
  145.       p = argv[i];
  146.       if (*p == '-') {
  147.      ++p;
  148.      while (c = *p++) {
  149.         switch(tolower(c)) {
  150.         case '?':
  151.            help(documentation);
  152.            break;
  153.         case 'C':
  154.         case 'c':
  155.            ++cflag;
  156.            break;
  157.         case 'D':
  158.         case 'd':
  159.            ++debug;
  160.            break;
  161.         case 'F':
  162.         case 'f':
  163.            ++fflag;
  164.            break;
  165.         case 'n':
  166.         case 'N':
  167.            ++nflag;
  168.            break;
  169.         case 'v':
  170.         case 'V':
  171.            ++vflag;
  172.            break;
  173.         default:
  174.            usage("Unknown flag");
  175.         }
  176.      }
  177.      argv[i] = 0;
  178.      --nfile;
  179.       } else if (!gotpattern) {
  180.      compile(p);
  181.      argv[i] = 0;
  182.      ++gotpattern;
  183.      --nfile;
  184.       }
  185.    }
  186.    if (!gotpattern)
  187.       usage("No pattern");
  188.    if (nfile == 0)
  189.       grep(stdin, 0);
  190.    else {
  191.       fflag = fflag ^ (nfile > 0);
  192.  
  193.       for (i=1; i < argc; ++i)
  194.       {
  195.      if (p = argv[i])
  196.      {
  197.          if (strchr(p, '?') || strchr(p, '*'))
  198.         {
  199. //-- JAJ wildcard support start (does not support directory wildcard ie c:\tmp\*.*
  200.             err = findfirst(p, &findbuf, 0);
  201.  
  202.             while (!err)
  203.             {
  204.                 if ((f=fopen(findbuf.ff_name, "r")) == NULL)
  205.                 {
  206.                    cant(findbuf.ff_name);
  207.                    break;
  208.                 }
  209.                 else {
  210.                    count++;
  211.                    grep(f, findbuf.ff_name);
  212.                    fclose(f);
  213.                 }
  214.                 err=findnext(&findbuf);
  215.             }
  216. //-- JAJ wildcard support end
  217.         }
  218.         else
  219.         {
  220.             if ((f=fopen(p, "r")) == NULL)
  221.                cant(p);
  222.             else {
  223.                  count++;
  224.                grep(f, p);
  225.                fclose(f);
  226.             }
  227.         }
  228.      }
  229.       }
  230.      if (count == 0) usage("No files found");
  231.    }
  232.  
  233. }
  234. /*******************************************************/
  235. file(s)
  236. char *s;
  237. {
  238. //-- JAJ better file handling for Zeus
  239. //   printf("File %s:\n", s);
  240.    printf("%-13s :", s);
  241. }
  242. /*******************************************************/
  243. cant(s)
  244. char *s;
  245. {
  246. //-- JAJ pipe to stdout so Zeus can see it    
  247. //   fprintf(stderr, "%s: cannot open\n", s);
  248.    fprintf(stdout, "%s: cannot open\n", s);
  249. }
  250. /*******************************************************/
  251. help(hp)
  252. char *hp;
  253. /*
  254.  * Give good help
  255.  */
  256. {
  257.    register char   **dp;
  258.    for (dp = hp; *dp; dp++)
  259.       printf("%s\n", *dp);
  260. }
  261. /*******************************************************/
  262. usage(s)
  263. char    *s;
  264. {
  265. //-- JAJ pipe to stdout so Zeus can see it    
  266. //   fprintf(stderr, "?GREP-E-%s\n", s);
  267. //   fprintf(stderr,
  268. //      "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  269.    fprintf(stdout, "?GREP-E-%s\n", s);
  270.    fprintf(stdout,
  271.       "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  272.    exit(1);
  273. }
  274. /*******************************************************/
  275. compile(source)
  276. char       *source;   /* Pattern to compile        */
  277. /*
  278.  * Compile the pattern into global pbuf[]
  279.  */
  280. {
  281.    register char  *s;          /* Source string pointer       */
  282.    register char  *lp;          /* Last pattern pointer       */
  283.    register int   c;          /* Current character       */
  284.    int          o;          /* Temp               */
  285.    char       *spp;       /* Save beginning of pattern */
  286.    char       *cclass();  /* Compile class routine       */
  287.    s = source;
  288.    if (debug)
  289.       printf("Pattern = \"%s\"\n", s);
  290.    pp = pbuf;
  291.    while (c = *s++) {
  292.       /*
  293.        * STAR, PLUS and MINUS are special.
  294.        */
  295.       if (c == '*' || c == '+' || c == '-') {
  296.      if (pp == pbuf ||
  297.           (o=pp[-1]) == BOL ||
  298.           o == EOL ||
  299.           o == STAR ||
  300.           o == PLUS ||
  301.           o == MINUS)
  302.         badpat("Illegal occurrance op.", source, s);
  303.      store(ENDPAT);
  304.      store(ENDPAT);
  305.      spp = pp;         /* Save pattern end     */
  306.      while (--pp > lp)     /* Move pattern down     */
  307.         *pp = pp[-1];     /* one byte         */
  308.      *pp =     (c == '*') ? STAR :
  309.         (c == '-') ? MINUS : PLUS;
  310.      pp = spp;         /* Restore pattern end  */
  311.      continue;
  312.       }
  313.       /*
  314.        * All the rest.
  315.        */
  316.       lp = pp;           /* Remember start       */
  317.       switch(c) {
  318.       case '^':
  319.      store(BOL);
  320.      break;
  321.       case '$':
  322.      store(EOL);
  323.      break;
  324.       case '.':
  325.      store(ANY);
  326.      break;
  327.       case '[':
  328.      s = cclass(source, s);
  329.      break;
  330.       case ':':
  331.      if (*s) {
  332.         c = *s++;
  333.         switch(tolower(c)) {
  334.         case 'a':
  335.         case 'A':
  336.            store(ALPHA);
  337.            break;
  338.         case 'd':
  339.         case 'D':
  340.            store(DIGIT);
  341.            break;
  342.         case 'n':
  343.         case 'N':
  344.            store(NALPHA);
  345.            break;
  346.         case ' ':
  347.            store(PUNCT);
  348.            break;
  349.         default:
  350.            badpat("Unknown : type", source, s);
  351.         }
  352.         break;
  353.      }
  354.      else     badpat("No : type", source, s);
  355.       case '\\':
  356.      if (*s)
  357.         c = *s++;
  358.       default:
  359.      store(CHAR);
  360.      store(tolower(c));
  361.       }
  362.    }
  363.    store(ENDPAT);
  364.    store(0);            /* Terminate string     */
  365.    if (debug) {
  366.       for (lp = pbuf; lp < pp;) {
  367.      if ((c = (*lp++ & 0377)) < ' ')
  368.         printf("\\%o ", c);
  369.      else     printf("%c ", c);
  370.     }
  371.     printf("\n");
  372.    }
  373. }
  374. /*******************************************************/
  375. char *
  376. cclass(source, src)
  377. char       *source;   /* Pattern start -- for error msg.      */
  378. char       *src;      /* Class start           */
  379. /*
  380.  * Compile a class (within [])
  381.  */
  382. {
  383.    register char   *s;          /* Source pointer    */
  384.    register char   *cp;       /* Pattern start       */
  385.    register int    c;          /* Current character */
  386.    int           o;          /* Temp           */
  387.    s = src;
  388.    o = CLASS;
  389.    if (*s == '^') {
  390.       ++s;
  391.       o = NCLASS;
  392.    }
  393.    store(o);
  394.    cp = pp;
  395.    store(0);                  /* Byte count     */
  396.    while ((c = *s++) && c!=']') {
  397.       if (c == '\\') {                /* Store quoted char    */
  398.      if ((c = *s++) == '\0')      /* Gotta get something  */
  399.         badpat("Class terminates badly", source, s);
  400.      else     store(tolower(c));
  401.       }
  402.       else if (c == '-' &&
  403.         (pp - cp) > 1 && *s != ']' && *s != '\0') {
  404.      c = pp[-1];         /* Range start     */
  405.      pp[-1] = RANGE;     /* Range signal    */
  406.      store(c);         /* Re-store start  */
  407.      c = *s++;         /* Get end char and*/
  408.      store(tolower(c));     /* Store it        */
  409.       }
  410.       else {
  411.      store(tolower(c));     /* Store normal char */
  412.       }
  413.    }
  414.    if (c != ']')
  415.       badpat("Unterminated class", source, s);
  416.    if ((c = (pp - cp)) >= 256)
  417.       badpat("Class too large", source, s);
  418.    if (c == 0)
  419.       badpat("Empty class", source, s);
  420.    *cp = c;
  421.    return(s);
  422. }
  423. /*******************************************************/
  424. store(op)
  425. {
  426.    if (pp >= &pbuf[PMAX])
  427.       error("Pattern too complex\n");
  428.    *pp++ = op;
  429. }
  430. /*******************************************************/
  431. badpat(message, source, stop)
  432. char  *message;       /* Error message */
  433. char  *source;          /* Pattern start */
  434. char  *stop;          /* Pattern end   */
  435. {
  436.    register int    c;
  437. //-- JAJ pipe to stdout so Zeus can see it    
  438. //   fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  439. //   fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  440. //     stop-source, stop[-1]);
  441.    fprintf(stdout, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  442.    fprintf(stdout, "-GREP-E-Stopped at byte %d, '%c'\n",
  443.      stop-source, stop[-1]);
  444.    error("?GREP-E-Bad pattern\n");
  445. }
  446. /*******************************************************/
  447. grep(fp, fn)
  448. FILE       *fp;       /* File to process        */
  449. char       *fn;       /* File name (for -f option)  */
  450. /*
  451.  * Scan the file for the pattern in pbuf[]
  452.  */
  453. {
  454.    register int lno, count, m;
  455.    lno = 0;
  456.    count = 0;
  457.    while (fgets(lbuf, LMAX, fp)) {
  458.       ++lno;
  459.       m = match();
  460.       if ((m && !vflag) || (!m && vflag)) {
  461.      ++count;
  462.      if (!cflag) {
  463.         if (fflag && fn) {
  464.            file(fn);
  465. //-- JAJ better file handling for Zeus
  466. //           fn = 0;
  467.         }
  468.         if (nflag)
  469.            printf(" %5.5d | ", lno);
  470.         printf("%s\n", lbuf);
  471.      }
  472.       }
  473.    }
  474.    if (cflag) {
  475.       if (fflag && fn)
  476.      file(fn);
  477.       printf("%d\n", count);
  478.    }
  479. }
  480. /*******************************************************/
  481. match()
  482. /*
  483.  * Match the current line (in lbuf[]), return 1 if it does.
  484.  */
  485. {
  486.    register char   *l;          /* Line pointer        */
  487.    char *pmatch();
  488.    for (l = lbuf; *l; l++) {
  489.       if (pmatch(l, pbuf))
  490.      return(1);
  491.    }
  492.    return(0);
  493. }
  494. /*******************************************************/
  495. char *
  496. pmatch(line, pattern)
  497. char           *line;     /* (partial) line to match      */
  498. char           *pattern;  /* (partial) pattern to match   */
  499. {
  500.    register char   *l;          /* Current line pointer          */
  501.    register char   *p;          /* Current pattern pointer      */
  502.    register char   c;          /* Current character          */
  503.    char        *e;          /* End for STAR and PLUS match  */
  504.    int           op;          /* Pattern operation          */
  505.    int           n;          /* Class counter              */
  506.    char        *are;      /* Start of STAR match          */
  507.    l = line;
  508.    if (debug > 1)
  509.       printf("pmatch(\"%s\")\n", line);
  510.    p = pattern;
  511.    while ((op = *p++) != ENDPAT) {
  512.       if (debug > 1)
  513.      printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  514.            l-line, *l, *l, op);
  515.       switch(op) {
  516.       case CHAR:
  517.      if (tolower(*l) != *p++)
  518.         return(0);
  519.      l++;
  520.      break;
  521.       case BOL:
  522.      if (l != lbuf)
  523.         return(0);
  524.      break;
  525.       case EOL:
  526.      if (*l != '\0')
  527.         return(0);
  528.      break;
  529.       case ANY:
  530.      if (*l++ == '\0')
  531.         return(0);
  532.      break;
  533.       case DIGIT:
  534.      if ((c = *l++) < '0' || (c > '9'))
  535.         return(0);
  536.      break;
  537.       case ALPHA:
  538.      c = tolower(*l);
  539.      l++;
  540.      if (c < 'a' || c > 'z')
  541.         return(0);
  542.      break;
  543.       case NALPHA:
  544.      c = tolower(*l);
  545.      l++;
  546.      if (c >= 'a' && c <= 'z')
  547.         break;
  548.      else if (c < '0' || c > '9')
  549.         return(0);
  550.      break;
  551.       case PUNCT:
  552.      c = *l++;
  553.      if (c == 0 || c > ' ')
  554.         return(0);
  555.      break;
  556.       case CLASS:
  557.       case NCLASS:
  558.      c = tolower(*l);
  559.      l++;
  560.      n = *p++ & 0377;
  561.      do {
  562.         if (*p == RANGE) {
  563.            p += 3;
  564.            n -= 2;
  565.            if (c >= p[-2] && c <= p[-1])
  566.           break;
  567.         }
  568.         else if (c == *p++)
  569.            break;
  570.      } while (--n > 1);
  571.      if ((op == CLASS) == (n <= 1))
  572.         return(0);
  573.      if (op == CLASS)
  574.         p += n - 2;
  575.      break;
  576.       case MINUS:
  577.      e = pmatch(l, p);     /* Look for a match    */
  578.      while (*p++ != ENDPAT); /* Skip over pattern    */
  579.      if (e)          /* Got a match?    */
  580.         l = e;         /* Yes, update string    */
  581.      break;          /* Always succeeds    */
  582.       case PLUS:         /* One or more ...    */
  583.      if ((l = pmatch(l, p)) == 0)
  584.         return(0);         /* Gotta have a match    */
  585.       case STAR:         /* Zero or more ...    */
  586.      are = l;         /* Remember line start */
  587.      while (*l && (e = pmatch(l, p)))
  588.         l = e;         /* Get longest match    */
  589.      while (*p++ != ENDPAT); /* Skip over pattern    */
  590.      while (l >= are) {     /* Try to match rest    */
  591.         if (e = pmatch(l, p))
  592.            return(e);
  593.         --l;         /* Nope, try earlier    */
  594.      }
  595.      return(0);         /* Nothing else worked */
  596.       default:
  597.      printf("Bad op code %d\n", op);
  598.      error("Cannot happen -- match\n");
  599.       }
  600.    }
  601.    return(l);
  602. }
  603. /*******************************************************/
  604. error(s)
  605. char *s;
  606. {
  607. //-- JAJ pipe to stdout so Zeus can see it    
  608. //   fprintf(stderr, "%s", s);
  609.    fprintf(stdout, "%s", s);
  610.    exit(1);
  611. }
  612. 
  613.